import pygame, sys, random, time, copy

#常量
from pygame.locals import *
UP, DOWN, LEFT, RIGHT = '向上', '向下', '向左', '向右'
#               R    G    B
BRIGHTBLUE  = (  0,   0, 255)
WHITE       = (255, 255, 255)
BGCOLOR = BRIGHTBLUE
TEXTCOLOR = WHITE
FPS = 30
WINDOWWIDTH = 800
WINDOWHEIGHT = 600
BOARDWIDTH = 7
BOARDHEIGHT = 8
SPACESIZE = 50
XMARGIN = (WINDOWWIDTH - (SPACESIZE * BOARDWIDTH)) //2
YMARGIN = (WINDOWHEIGHT - (SPACESIZE * BOARDHEIGHT)) //2

WINDOWCAPTION = '四子成线'
FONTFILENAME = 'res/msyh.ttc'
FONTSIZE = 20
ICONFILENAME = 'res/gameicon.png'



RED = '红色'
BLACK= '黑色'
EMPTY = None
HUMAN = '玩家'
COMPUTER = '计算机'
DIFFICULTY = 2 #计算机递归决定一步走法时的预测深度，如果设成3运算量就爆炸了，非常慢
#终止程序
def terminate():
    pygame.quit()
    sys.exit()
#关闭窗口或按Esc键退出程序
def checkForQuit():
    for event in pygame.event.get(QUIT):
        terminate()
    for event in pygame.event.get(KEYUP):
        if event.key == K_ESCAPE:
            terminate()
        else:
            pygame.event.post(event)
def getNewBoard():
    board = []
    for x in range(BOARDWIDTH):
        board.append([EMPTY] * BOARDHEIGHT)
    return board
def drawBoard(board, extraToken=None):
    DISPLAYSURFACE.fill (BGCOLOR)
    spaceRect = pygame.Rect(0, 0, SPACESIZE, SPACESIZE)
    for x in range(BOARDWIDTH):
        for y in range(BOARDHEIGHT):
            spaceRect.topleft = (XMARGIN + SPACESIZE*x, YMARGIN + SPACESIZE*y)
            if board[x][y] == RED:
                DISPLAYSURFACE.blit(REDTOKENIMG, spaceRect)
            elif board[x][y] == BLACK:
                DISPLAYSURFACE.blit(BLACKTOKENIMG, spaceRect)
    if extraToken:
        if extraToken['color'] == RED:
            DISPLAYSURFACE.blit(REDTOKENIMG, (extraToken['x'], extraToken['y'], SPACESIZE, SPACESIZE))
        elif extraToken['color'] == BLACK:
            DISPLAYSURFACE.blit(BLACKTOKENIMG, (extraToken['x'], extraToken['y'], SPACESIZE, SPACESIZE))
    for x in range(BOARDWIDTH):
        for y in range(BOARDHEIGHT):
            spaceRect.topleft = (XMARGIN + SPACESIZE*x, YMARGIN + SPACESIZE*y)
            DISPLAYSURFACE.blit(BOARDIMG, spaceRect)
    DISPLAYSURFACE.blit(REDTOKENIMG, REDPILERECT)
    DISPLAYSURFACE.blit(BLACKTOKENIMG, BLACKPILERECT)
    
def getLowestEmptySpace(board, column):
    for y in range(BOARDHEIGHT-1, -1, -1):
        if board[column][y] == EMPTY:
            return y
    return -1 #此列已满
def makeMove(board, player, column):
    lowest = getLowestEmptySpace(board, column)
    if lowest != -1:
        board[column][lowest] = player
def getColumnNum(positionxy):
    x, y = positionxy
    if x > XMARGIN and x < WINDOWWIDTH - XMARGIN and y < YMARGIN:
        return int((x - XMARGIN) / SPACESIZE)
    return None
def isValidMove(board, column):
    if column < 0 or column >= BOARDWIDTH or board[column][0] != EMPTY:
        return False
    return True
def getHumanMove(board, isFirstMove):
    draggingToken = False
    tokenx, tokeny = None, None
    mousePosition = None
    while True:
        checkForQuit()#检查来自鼠标或键盘的退出事件，结束程序
        #检测鼠标动作和键盘操作事件，进行登记或直接处理
        for event in pygame.event.get():
            if event.type == MOUSEBUTTONDOWN:#按下鼠标键，既是点击的开始，又是拖拽的开始
                draggingToken = True
                mousePosition = event.pos
            elif event.type == MOUSEMOTION and draggingToken:
                mousePosition = event.pos
            elif event.type == MOUSEBUTTONUP:
                mousePosition = event.pos
                column = getColumnNum(mousePosition)
                if(column != None):
                    if isValidMove(board, column):
                        animateDroppingToken(board, column, RED)
                        board[column][getLowestEmptySpace(board, column)] = RED
                        drawBoard(board)
                        pygame.display.update()
                        return
                draggingToken = False
                mousePosition = None
        if mousePosition:
            x, y = mousePosition
            drawBoard(board, {'x':x - SPACESIZE//2, 'y':y - SPACESIZE//2, 'color':RED})
        else:
            drawBoard(board)
        pygame.display.update()
        FPSCLOCK.tick()
def animateComputerMoving(board, column):
    x, y = BLACKPILERECT.left, BLACKPILERECT.top
    speed = 1.0
    while y > (YMARGIN-SPACESIZE):
        y -= int(speed)
        speed += 0.5
        drawBoard(board, {'x':x, 'y':y, 'color':BLACK})
        pygame.display.update()
        FPSCLOCK.tick()
    y = YMARGIN - SPACESIZE
    speed = 1.0
    while x > (XMARGIN + column * SPACESIZE):
        x -= int(speed)
        speed += 0.5
        drawBoard(board, {'x':x, 'y':y, 'color':BLACK})
        pygame.display.update()
        FPSCLOCK.tick()
    animateDroppingToken(board, column, BLACK)
def animateDroppingToken(board, column, color):
    x = XMARGIN + column * SPACESIZE
    y = YMARGIN - SPACESIZE
    dropSpeed = 1.0
    lowestEmptySpace = getLowestEmptySpace(board, column)
    while True:
        y += int(dropSpeed)
        dropSpeed += 0.5
        if (y-YMARGIN)//SPACESIZE >= lowestEmptySpace:
            return
        drawBoard(board, {'x':x, 'y':y, 'color':color})
        pygame.display.update()
        FPSCLOCK.tick()
def isWinner(board, tile):
    # 水平方向找
    for x in range(BOARDWIDTH - 3):
        for y in range(BOARDHEIGHT):
            if tile == board[x][y] == board[x+1][y] == board[x+2][y] == board[x+3][y]:
                return True
    # 垂直方向找
    for x in range(BOARDWIDTH):
        for y in range(BOARDHEIGHT - 3):
            if tile == board[x][y] == board[x][y+1] == board[x][y+2] == board[x][y+3]:
                return True
    # / 方向找
    for x in range(BOARDWIDTH - 3):
        for y in range(3, BOARDHEIGHT):
            if tile == board[x][y] == board[x+1][y-1] == board[x+2][y-2] == board[x+3][y-3]:
                return True
    # \ 方向找
    for x in range(BOARDWIDTH - 3):
        for y in range(BOARDHEIGHT - 3):
            if tile == board[x][y] == board[x+1][y+1] == board[x+2][y+2] == board[x+3][y+3]:
                return True
        
    return False
def isBoardFull(board):
    for x in range(BOARDWIDTH):
        for y in range(BOARDHEIGHT):
            if EMPTY == board[x][y]:
                return False
    return True
def getPotentialMoves(board, tile, lookAhead):
    # 递归预见未来的所有最佳步骤
    if lookAhead== 0 or isBoardFull(board):
        return [0] * BOARDWIDTH
    if tile == RED:
        enemyTile = BLACK
    else:
        enemyTile = RED
    
    potentialMoves = [0] * BOARDWIDTH #各列都行，无优劣之分（权重为0）
    for firstMove in range(BOARDWIDTH):
        dupeBoard = copy.deepcopy(board)
        if not isValidMove(dupeBoard, firstMove):
            continue
        makeMove(dupeBoard, tile, firstMove)
        if isWinner(dupeBoard, tile):#如果走了这步，自己能赢，这就是好步骤（权重为1）
            potentialMoves[firstMove] = 1
            break
        else:#如果走了这步，对手能赢，这就是坏步骤（权重为-1）
            if isBoardFull(dupeBoard):
                potentialMoves[firstMove] = 0
            else:#预计对手走的步骤
                for counterMove in range(BOARDWIDTH):
                    dupeBoard2 = copy.deepcopy(dupeBoard)
                    if not isValidMove(dupeBoard2, counterMove):
                        continue
                    makeMove(dupeBoard2, enemyTile, counterMove)
                    if isWinner(dupeBoard2, enemyTile):
                        potentialMoves[firstMove] = -1
                        break
                    else:
                        results = getPotentialMoves(dupeBoard2, tile, lookAhead-1)
                        potentialMoves[firstMove] += sum(results)/BOARDWIDTH/BOARDWIDTH
    return potentialMoves
def getComputerMove(board):
    potentialMoves = getPotentialMoves(board, BLACK, DIFFICULTY)
    bestMoveFitness = -1
    for i in range(len(potentialMoves)):
        if potentialMoves[i] > bestMoveFitness and isValidMove(board, i):
            bestMoveFitness = potentialMoves[i]
    bestMoves = []
    for i in range(len(potentialMoves)):
        if potentialMoves[i] == bestMoveFitness and isValidMove(board, i):
            bestMoves.append(i)
    return random.choice(bestMoves)
def runGame():
    score, helpMode, gameIsOver = 0, True, False
    mouseDownPosition, mouseUpPosition, mousePositin = None, None, None
    turn = COMPUTER
    mainBoard = getNewBoard()
    while True:
        #在内存绘制
        
        drawBoard(mainBoard)
        checkForQuit()#检查来自鼠标或键盘的退出事件，结束程序
        
        if turn == HUMAN:
            getHumanMove(mainBoard, helpMode)
            if isWinner(mainBoard, RED):
                winnerImage = HUMANWINNERIMG
                break
            turn = COMPUTER
        else:
            column = getComputerMove(mainBoard)
            animateComputerMoving(mainBoard, column)
            makeMove(mainBoard, BLACK, column)
            if isWinner(mainBoard, BLACK):
                winnerImage = COMPUTERWINNERIMG
                break
            turn = HUMAN
        if isBoardFull(mainBoard):
            winnerImage = TIEWINNERIMG
            break            

    while True:
        drawBoard(mainBoard)
        DISPLAYSURFACE.blit(winnerImage, WINNERRECT)
        pygame.display.update()
        FPSCLOCK.tick(FPS)
        checkForQuit()#检查来自鼠标或键盘的退出事件，结束程序
        for event in pygame.event.get():
            if event.type == MOUSEBUTTONUP:
                return
def fourinarow():
    #初始化    
    global DISPLAYSURFACE, FPSCLOCK, BASICFONT, BASICSOUND
    global BOARDIMG, REDTOKENIMG, BLACKTOKENIMG, REDPILERECT, BLACKPILERECT
    global WINNERRECT, TIEWINNERIMG, HUMANWINNERIMG, COMPUTERWINNERIMG
    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURFACE = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
    pygame.display.set_caption(WINDOWCAPTION)
    pygame.display.set_icon(pygame.image.load(ICONFILENAME))    
    BASICFONT = pygame.font.Font(FONTFILENAME, FONTSIZE)

    BOARDIMG = pygame.image.load('res/4row_board.png')
    BOARDIMG = pygame.transform.smoothscale(BOARDIMG, (SPACESIZE, SPACESIZE))
    REDTOKENIMG = pygame.image.load('res/4row_red.png')
    REDTOKENIMG = pygame.transform.smoothscale(REDTOKENIMG, (SPACESIZE, SPACESIZE))
    BLACKTOKENIMG = pygame.image.load('res/4row_black.png')
    BLACKTOKENIMG = pygame.transform.smoothscale(BLACKTOKENIMG, (SPACESIZE, SPACESIZE))
    REDPILERECT = pygame.Rect(SPACESIZE//2, WINDOWHEIGHT-SPACESIZE*3//2, SPACESIZE, SPACESIZE)
    BLACKPILERECT = pygame.Rect(WINDOWWIDTH-SPACESIZE*3//2, WINDOWHEIGHT-SPACESIZE*3//2, SPACESIZE, SPACESIZE)
    HUMANWINNERIMG = pygame.image.load('res/4row_humanwinner.png')
    WINNERRECT = HUMANWINNERIMG.get_rect()
    WINNERRECT.center = (WINDOWWIDTH//2, 80)
    TIEWINNERIMG = pygame.image.load('res/4row_tie.png')
    TIEWINNERIMG = pygame.transform.smoothscale(TIEWINNERIMG, (WINNERRECT.width, WINNERRECT.height))
    COMPUTERWINNERIMG = pygame.image.load('res/4row_computerwinner.png')
    COMPUTERWINNERIMG = pygame.transform.smoothscale(COMPUTERWINNERIMG, (WINNERRECT.width, WINNERRECT.height))
    while True:
        runGame()
  
if __name__ == '__main__':
    fourinarow()